//
//  LMSectionCollectionView.m
//  LMSectionViewModel
//
//  Created by Arc Lin on 2021/2/2.
//

#import "YKSectionCollectionView.h"
#import "YKSectionCollectionViewModel.h"
#import "YKSectionReuseViewProtocol.h"
#import <ReactiveObjC/ReactiveObjC.h>

@interface YKSectionCollectionView()<UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout>

@property (nonatomic, strong) YKSectionCollectionViewModel *viewModel;

@property (nonatomic, strong) RACSubject *errorSubject;

@property (nonatomic, strong) RACSubject *reloadSubject;

@end

@implementation YKSectionCollectionView

- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(nonnull UICollectionViewLayout *)layout sectionViewModels:(NSArray<id<YKSectionViewModelProtocol>> *)sectionViewModels {
    if (self = [super initWithFrame:frame collectionViewLayout:layout]) {
        [self setSectionViewModels:sectionViewModels];
        self.delegate = self;
        self.dataSource = self;
        self.backgroundColor = UIColor.clearColor;
        [self registerReuseViews];
        [self registerClass:UICollectionReusableView.class forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"UICollectionReusableView"];
        [self registerClass:UICollectionReusableView.class forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"UICollectionReusableView"];
    }
    return self;
}

#pragma mark - UICollectionViewDataSource

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return self.viewModel.dataSources.count;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.viewModel.dataSources[section].count;
}

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell<YKSectionReuseViewProtocol> *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[self.viewModel cellIdAtIndexPath:indexPath] forIndexPath:indexPath];
    if ([cell respondsToSelector:@selector(loadDataWithViewModel:atIndexPath:)]) {    
        [cell loadDataWithViewModel:self.viewModel.showingViewModels[indexPath.section] atIndexPath:indexPath];
    } else {
#ifdef DEBUG
        //
        NSLog(@"❌ %@缺少对loadDataWithViewModel:atIndexPath:的实现",NSStringFromClass(cell.class));
#else
#endif
    }
    return cell;
}

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    NSString *headerName = [self.viewModel sectionHeaderIdAtIndexPath:indexPath];
    NSString *footerName = [self.viewModel sectionFooterIdAtIndexPath:indexPath];
    if (headerName && headerName.length > 0 && [kind isEqualToString:UICollectionElementKindSectionHeader]) {
        UICollectionReusableView<YKSectionReuseViewProtocol> *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerName forIndexPath:indexPath];
        if ([headerView respondsToSelector:@selector(loadDataWithViewModel:atIndexPath:)]) {
            [headerView loadDataWithViewModel:self.viewModel.showingViewModels[indexPath.section] atIndexPath:indexPath];
        } else {
#ifdef DEBUG
            //
            NSLog(@"❌ %@缺少对loadDataWithViewModel:atIndexPath:的实现",headerName);
#else
#endif
        }
        return headerView;
    }
    
     if (footerName && footerName.length > 0 && [kind isEqualToString:UICollectionElementKindSectionFooter]) {
        UICollectionReusableView<YKSectionReuseViewProtocol> *footerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:footerName forIndexPath:indexPath];
         if ([footerView respondsToSelector:@selector(loadDataWithViewModel:atIndexPath:)]) {
             [footerView loadDataWithViewModel:self.viewModel.showingViewModels[indexPath.section] atIndexPath:indexPath];
         } else {
#ifdef DEBUG
             //
             NSLog(@"❌ %@缺少对loadDataWithViewModel:atIndexPath:的实现",footerName);
#else
#endif
         }
        return footerView;
    }
    // 保底策略
    return [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"UICollectionReusableView" forIndexPath:indexPath];
}

#pragma mark - UICollectionViewDelegateFlowLayout

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [self.viewModel cellSizeWithCollectionViewWidth:collectionView.frame.size.width atIndexPath:indexPath];
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
    return [self.viewModel headerSizeWithCollectionViewWidth:collectionView.frame.size.width section:section];
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section
{
    return [self.viewModel footerSizeWithCollectionViewWidth:collectionView.frame.size.width section:section];
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    return [self.viewModel insetAtSection:section];
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
    return [self.viewModel minimumLineSpacingAtSection:section];
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
    return [self.viewModel minimumInteritemSpacingAtSection:section];
}

#pragma mark - UICollectionViewDelegate

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    [self.viewModel didSelectItemAtIndexPath:indexPath];
}

#pragma mark - ScrollViewDelegate

- (void)safeScrollViewDelegateInvokeWithSelector:(SEL)selector
{
    if ([self.scrollViewDelegate respondsToSelector:selector]) {
        [self.scrollViewDelegate performSelector:selector withObject:self];
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    [self safeScrollViewDelegateInvokeWithSelector:@selector(scrollViewDidScroll:)];
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    [self safeScrollViewDelegateInvokeWithSelector:@selector(scrollViewDidScroll:)];
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [self safeScrollViewDelegateInvokeWithSelector:@selector(scrollViewWillBeginDragging:)];
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if ([self.scrollViewDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) {
        [self.scrollViewDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    if ([self.scrollViewDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) {
        [self.scrollViewDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
    }
}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
    [self safeScrollViewDelegateInvokeWithSelector:@selector(scrollViewWillBeginDecelerating:)];
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [self safeScrollViewDelegateInvokeWithSelector:@selector(scrollViewDidEndDecelerating:)];
}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
    [self safeScrollViewDelegateInvokeWithSelector:@selector(scrollViewDidEndScrollingAnimation:)];
}

- (nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    if ([self.scrollViewDelegate respondsToSelector:@selector(viewForZoomingInScrollView:)]) {
        return [self.scrollViewDelegate viewForZoomingInScrollView:scrollView];
    }
    return nil;
}

- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view {
    if ([self.scrollViewDelegate respondsToSelector:@selector(scrollViewWillBeginZooming:withView:)]) {
        [self.scrollViewDelegate scrollViewWillBeginZooming:scrollView withView:view];
    }
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale {
    if ([self.scrollViewDelegate respondsToSelector:@selector(scrollViewDidEndZooming:withView:atScale:)]) {
        [self.scrollViewDelegate scrollViewDidEndZooming:scrollView withView:view atScale:scale];
    }
}

- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
    if ([self.scrollViewDelegate respondsToSelector:@selector(scrollViewShouldScrollToTop:)]) {
        return [self.scrollViewDelegate scrollViewShouldScrollToTop:scrollView];
    }
    return YES;
}

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
    [self safeScrollViewDelegateInvokeWithSelector:@selector(scrollViewDidScrollToTop:)];
}

- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView {
    [self safeScrollViewDelegateInvokeWithSelector:@selector(scrollViewDidChangeAdjustedContentInset:)];
}

#pragma mark - Event

- (void)reloadViewModelData {
    [self.viewModel reloadData];
}

- (void)reloadViewModelDataAtSections:(NSIndexSet *)sections {
    [self.viewModel reloadDataAtSection:sections];
}

- (BOOL)handleRouterEvent:(NSString *)event userInfo:(NSDictionary *)userInfo {
    return [self.viewModel handleRouterEvent:event userInfo:userInfo handleController:self.handleViewController];
}

- (void)resetViewModels:(NSArray<id<YKSectionViewModelProtocol>> *)viewModels needRequest:(BOOL)needRequest {
    [self.viewModel resetSectionViewModels:viewModels needRequest:needRequest];
    [self registerReuseViews];
}

- (void)insertSectionViewModel:(id<YKSectionViewModelProtocol>)viewModel atIndex:(NSInteger)index {
    [self.viewModel insertSectionViewModel:viewModel atIndex:index];
    [self registerReuseViews];
}

- (void)deleteSectionViewModel:(id<YKSectionViewModelProtocol>)viewModel {
    [self.viewModel deleteSectionViewModel:viewModel];
    [self registerReuseViews];
}

#pragma mark - setter & getter

- (void)registerReuseViews {
    for (NSDictionary *cellInfo in self.viewModel.cellInfos) {
        [self registerClass:NSClassFromString(cellInfo[className]) forCellWithReuseIdentifier:cellInfo[idName]];
    }
    for (NSDictionary *sectionInfo in self.viewModel.sectionHeaderViewInfos) {
        [self registerClass:NSClassFromString(sectionInfo[className]) forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:sectionInfo[idName]];
    }
    
    for (NSDictionary *sectionInfo in self.viewModel.sectionFooterViewInfos) {
        [self registerClass:NSClassFromString(sectionInfo[className]) forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:sectionInfo[idName]];
    }
}

- (void)setSectionViewModels:(NSArray<id<YKSectionViewModelProtocol>> *)sectionViewModels {
    self.viewModel = [[YKSectionCollectionViewModel alloc] initWithSectionViewModels:sectionViewModels];
    @weakify(self);
    [[[self.viewModel.reloadSubject deliverOnMainThread] throttle:0.3] subscribeNext:^(id  _Nullable x) {
        @strongify(self);
        [self.reloadSubject sendNext:nil];
        [self reloadData];
    }];
    [self.viewModel.errorSubject subscribe:self.errorSubject];
}

- (void)setHandleViewController:(void (^)(UIViewController * _Nonnull, YKSectionCollectionViewPushType))handleViewController {
    _handleViewController = handleViewController;
    self.viewModel.handleViewController = handleViewController;
}

- (RACSubject *)reloadSubject {
    if (!_reloadSubject) {
        _reloadSubject = [RACSubject subject];
    }
    return _reloadSubject;
}

- (RACSubject *)errorSubject {
    if (!_errorSubject) {
        _errorSubject = [RACSubject subject];
    }
    return _errorSubject;
}

- (NSArray *)currentSectionViewModels {
    return self.viewModel.viewModels;
}

- (void)dealloc {
    
}

@end
